<?php
declare(strict_types=1);
namespace Aincrid\MySwoole\Router;

use Aincrid\MySwoole\Middleware;
use Aincrid\MySwoole\Request;
use Aincrid\MySwoole\Response;
use Aincrid\MySwoole\Facade\Container;
use Aincrid\MySwoole\Facade\Config;
use App\Controller;

class Router
{
    public array $gets = [];

    public array $posts = [];

    public array $puts = [];

    public array $deletes = [];

    public function group(string $path, array $configs, \Closure $handler): RouterGroup
    {
        $routerGroup = new RouterGroup($path);
        foreach ($configs as $key => $config) {
            $method = 'set' . ucfirst($key);
            $routerGroup->{$method}($config);
        }
        $handler($routerGroup);
        return $routerGroup;
    }

    public function addRoute(string $path, string $method, \Closure|string $handler, RouterGroup $routerGroup = null): RouterItem
    {
        ///api/user/:id
        $routerItem = new RouterItem($path, $method, $handler, $routerGroup);
        if (!is_null($routerGroup)) {
            $routerItem->setPath($routerGroup->getPath() . $path);
            $routerItem->setNamespace($routerGroup->getNamespace());
            $routerItem->appendMiddleware(...$routerGroup->getMiddleware());
        }
        $fullPaths = explode('/', $routerItem->getPath());

        $regexPaths = [];
        foreach ($fullPaths as $fullPath) {
            $patern = '/\:([a-zA-Z]+)(\\\\([ds]))?/';
            $result = preg_match($patern, $fullPath, $matches);
            if ($result) {
                $paramName = $matches[1];
                $dataType = $matches[3] ?? '';
                $routerItem->addDynamicParamKey($paramName);
                if ($dataType == 'd') {
                    $fullPath = '(\\d+)';
                } else {
                    $fullPath = '(\\w+)';
                }
            }
            $regexPaths[] = $fullPath;
        }
        $routerItem->setRegexPaths('{^\\' . implode('/', $regexPaths) . '$}');

        $method = strtolower($method);
        $this->{$method . 's'}[] = $routerItem;
        return $routerItem;
    }

    public function dispatch(string $url, Request $request, Response $response)
    {
        $routerItem = $this->matchRoute($request->method(), $url);
        $middlewares = Config::get('middleware');

        if (is_null($routerItem)) {
            $middlewares[] = $handler = Controller::class . '@notFound';
        } else {
            $request->params = array_combine($routerItem->getDynamicParamKey(), $routerItem->getDynamicParamValue());
            $middlewares = array_merge($middlewares, $routerItem->getMiddleware());
            $middlewares[] = $handler = $routerItem->getHandler();
        }
        $index = 0;
        $next = function ($request) use ($middlewares, $handler, $response, &$index, &$next)
        {
            if ($index > count($middlewares) - 1) {
                return;
            }
            $middleware = $middlewares[$index];

            $index++;
            if ($middleware == $handler) {
                return $this->getResponse($handler, $request, $response)($request, $next);
            } else {
                return (new $middleware())($request, $next);
            }
        };
        $next($request);
    }


    public function getResponse(string|\Closure $handler, Request $request, Response $response)
    {
        return function ($request, $next) use ($handler, $response)
        {
            return Container::invoke($handler, ['request' => $request, 'response' => $response]);
        };
    }


    /**
     * 解析动态路由
     * :id 带参数 \d 数字 \s 字符串 
     * ?id 可选参数
     * 
     */
    protected function matchRoute(string $method, string $paths): ?RouterItem
    {
        $method = strtolower($method);
        $routerItems = $this->{$method . 's'};
        $matches = [];
        foreach ($routerItems as $routerItem) {
            $result = preg_match($routerItem->getRegexPaths(), $paths, $matches);
            if ($result) {
                unset($matches[0]);
                $routerItem->setDynaminParamValue($matches);
                return $routerItem;
            }
        }

        return null;
    }
}